<template>
  <div class="waterfall-container" ref="containerRef">
    <div 
      v-for="(item, index) in items" 
      :key="item.id || index"
      class="waterfall-item"
      :style="getItemStyle()"
      ref="itemRefs"
    >
      <slot name="item" :item="item" :index="index"></slot>
    </div>
    <div v-if="loading" class="loading-indicator">
      <div class="loading-spinner"></div>
      <p>加载中...</p>
    </div>
    <div v-if="noMoreData" class="no-more">
      <p>没有更多内容了</p>
    </div>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted, nextTick, watch } from 'vue'

interface WaterfallItem {
  id?: string | number
  [key: string]: any
}

interface Props {
  items: WaterfallItem[]
  columnWidth?: number
  gap?: number
  loading?: boolean
  noMoreData?: boolean
  threshold?: number
}

const props = withDefaults(defineProps<Props>(), {
  columnWidth: 300,
  gap: 20,
  loading: false,
  noMoreData: false,
  threshold: 100
})

const emit = defineEmits<{
  loadMore: []
}>()

const containerRef = ref<HTMLElement>()
const itemRefs = ref<HTMLElement[]>([])
const columnHeights = ref<number[]>([])
const containerWidth = ref(0)
const columns = ref(1)
const resizeObserver = ref<ResizeObserver>()

// 响应式列数计算
const calculateColumns = () => {
  if (!containerRef.value) return
  containerWidth.value = containerRef.value.clientWidth
  
  // 响应式列数计算
  if (containerWidth.value < 768) {
    columns.value = Math.max(1, Math.floor(containerWidth.value / (props.columnWidth + props.gap)))
  } else if (containerWidth.value < 1200) {
    columns.value = Math.max(2, Math.floor(containerWidth.value / (props.columnWidth + props.gap)))
  } else {
    columns.value = Math.max(3, Math.floor((containerWidth.value + props.gap) / (props.columnWidth + props.gap)))
  }
  
  // 限制最大列数
  columns.value = Math.min(columns.value, 6)
  
  // 初始化列高度
  columnHeights.value = new Array(columns.value).fill(0)
}

// 获取项目样式
const getItemStyle = () => {
  if (columnHeights.value.length === 0) {
    return {
      position: 'absolute' as const,
      left: '0px',
      top: '0px',
      width: `${props.columnWidth}px`,
      transform: 'translateZ(0)',
      willChange: 'transform'
    }
  }
  
  // 找到最短的列
  const minHeight = Math.min(...columnHeights.value)
  const columnIndex = columnHeights.value.indexOf(minHeight)
  
  // 计算位置
  const left = columnIndex * (props.columnWidth + props.gap)
  const top = minHeight
  
  return {
    position: 'absolute' as const,
    left: `${left}px`,
    top: `${top}px`,
    width: `${props.columnWidth}px`,
    transform: 'translateZ(0)',
    willChange: 'transform'
  }
}

// 更新项目位置
const updateItems = async () => {
  if (!itemRefs.value.length) return
  
  await nextTick()
  
  // 重置列高度
  columnHeights.value = new Array(columns.value).fill(0)
  
  // 更新每个项目的位置
  itemRefs.value.forEach((item) => {
    if (!item) return
    
    // 找到最短的列
    const minHeight = Math.min(...columnHeights.value)
    const columnIndex = columnHeights.value.indexOf(minHeight)
    
    // 更新位置
    const left = columnIndex * (props.columnWidth + props.gap)
    const top = minHeight
    
    item.style.position = 'absolute'
    item.style.left = `${left}px`
    item.style.top = `${top}px`
    item.style.width = `${props.columnWidth}px`
    item.style.transform = 'translateZ(0)'
    item.style.willChange = 'transform'
    
    // 更新列高度
    const itemHeight = item.offsetHeight
    columnHeights.value[columnIndex] = minHeight + itemHeight + props.gap
  })
  
  // 更新容器高度
  if (containerRef.value) {
    const maxHeight = Math.max(...columnHeights.value)
    containerRef.value.style.height = `${maxHeight}px`
  }
}

// 防抖处理滚动
let scrollTimer: number
const handleScroll = () => {
  if (props.loading || props.noMoreData) return
  
  const scrollTop = window.pageYOffset || document.documentElement.scrollTop
  const windowHeight = window.innerHeight
  const documentHeight = document.documentElement.scrollHeight
  
  if (scrollTop + windowHeight >= documentHeight - props.threshold) {
    emit('loadMore')
  }
}

const debouncedScroll = () => {
  clearTimeout(scrollTimer)
  scrollTimer = window.setTimeout(handleScroll, 100)
}

// 监听窗口大小变化
const setupResizeObserver = () => {
  if (!containerRef.value) return
  
  resizeObserver.value = new ResizeObserver(() => {
    calculateColumns()
    updateItems()
  })
  
  resizeObserver.value.observe(containerRef.value)
}

// 监听项目变化
watch(() => props.items, () => {
  nextTick(() => {
    updateItems()
  })
}, { deep: true })

onMounted(() => {
  calculateColumns()
  updateItems()
  setupResizeObserver()
  window.addEventListener('scroll', debouncedScroll, { passive: true })
})

onUnmounted(() => {
  if (resizeObserver.value) {
    resizeObserver.value.disconnect()
  }
  window.removeEventListener('scroll', debouncedScroll)
  clearTimeout(scrollTimer)
})

// 暴露方法给父组件
defineExpose({
  updateItems,
  calculateColumns
})
</script>

<style scoped>
.waterfall-container {
  position: relative;
  width: 100%;
  margin: 0 auto;
}

.waterfall-item {
  transition: all 0.3s ease;
  border-radius: 12px;
  overflow: hidden;
  background: white;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.waterfall-item:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.15);
}

.loading-indicator {
  grid-column: 1 / -1;
  text-align: center;
  padding: 20px;
  color: #666;
}

.loading-spinner {
  width: 30px;
  height: 30px;
  border: 3px solid #f3f3f3;
  border-top: 3px solid #42b983;
  border-radius: 50%;
  animation: spin 1s linear infinite;
  margin: 0 auto 10px;
}

.no-more {
  grid-column: 1 / -1;
  text-align: center;
  padding: 20px;
  color: #999;
  font-size: 14px;
}

@keyframes spin {
  0% { transform: rotate(0deg); }
  100% { transform: rotate(360deg); }
}
</style>